-module(np_server).
-export([start/0]).

start() ->
	io:format("Start NetworkProxy Server...~n"),
	{ok, ListenSocket} = gen_tcp:listen(8080, [binary, inet, {reuseaddr, true}, {active, false}]),
	io:format("Listen...~n"),
	{ok, Socket} = gen_tcp:accept(ListenSocket),
	{ok, {Address, Port}} = inet:peername(Socket),
	io:format("New Client Connected(~p:~p).~n", [Address, Port]),
	{ok, Package} = gen_tcp:recv(Socket, 3),
	<<1:8/big, LoginNameLen:16/big>> = Package,
	{ok, LoginName} = gen_tcp:recv(Socket, LoginNameLen),
	io:format("[~p] Login.~n", [LoginName]),
	gen_tcp:send(Socket, <<1:1/big, 0:7/big, 127:8/big, 0:8/big, 0:8/big, 1:8/big, 23:16/big>>),
	{ok, ClientState} = gen_tcp:recv(Socket, 4),
	<<2:8/big, State:8/big, MsgLen:16/big>> = ClientState,
	if
		State == 1 ->
			io:format("NP success.~n"),
			FPid = self(),
			LPid = spawn(fun() -> local_proxy_listen(Socket, FPid) end),
			inet:setopts(Socket, [{active, true}]),
			receive
				{localproxy, LocalSocket} ->
					np_loop(Socket, LocalSocket, LPid)
			end;
		true ->
			{ok, ErrMsg} = gen_tcp:recv(Socket, MsgLen),
			io:format("NP Error:~p~n", [ErrMsg])
	end.

np_loop(Socket, LPSocket, LPid) ->
	receive
		{tcp, Socket, Binary} ->
			io:format("Network To Local Data Send.~n"),
			gen_tcp:send(LPSocket, Binary),
			np_loop(Socket, LPSocket, LPid);
		{tcp_closed,Socket} ->
			io:format("Network Socket Closed.~n"),
			gen_tcp:close(Socket),
			LPid ! {networkproxy, self(), shutdown};
		{localproxy, LPid, shutdown} ->
			io:format("Local Socket Closed.~n"),
			gen_tcp:shutdown(Socket, read_write),
			gen_tcp:close(Socket)
	end.

local_proxy_listen(NPSocket, FPid) ->
	io:format("start local app proxy...~n"),
	{ok, LocalProxyListenSocket} = gen_tcp:listen(1234, [binary, inet, {reuseaddr, true}]),
	{ok, LocalSocket} = gen_tcp:accept(LocalProxyListenSocket),
	io:format("accept app client...~n"),
	FPid ! {localproxy, LocalSocket},
	local_proxy_loop(LocalSocket, NPSocket, FPid).
	
local_proxy_loop(Socket, NPSocket, FPid) ->
	receive
		{tcp, Socket, Binary} ->
			io:format("Local To Network Data Send.~n"),
			gen_tcp:send(NPSocket, Binary),
			local_proxy_loop(Socket, NPSocket, FPid);
		{tcp_closed,Socket} ->
			io:format("Local Socket Closed.~n"),
			gen_tcp:close(Socket),
			FPid ! {localproxy, self(), shutdown};
		{networkproxy, FPid, shutdown} ->
			io:format("Network Socket Closed.~n"),
			gen_tcp:shutdown(Socket, read_write),
			gen_tcp:close(Socket)
	end.
